library("easypackages")
library(tsibble)
library(tidyverse)
library(tidyquant)
library(plotly)
library(DT)
library(zoo)
library(lubridate)
library(fpp2)
library(fpp3)
library(scales)
library(patchwork)
library(feasts)
library(fable)
require(reshape)

packages("tidyverse", "tidyquant", "lubridate", "patchwork", "fpp2","fpp3","scales", "timetk")

Primer Serie de Tiempo

C <- read.csv('./RSCCASN.csv')
C
NA

Teoría

Ventas minoristas anticipadas: Tiendas de ropa y accesorios de ropa

Las ventas minoristas Caracterizado por ser un negocio pequeño de origen familiar, se basa en la venta en menudeo o detallista es la empresa comercial o persona en régimen de autónomo que vende productos al consumidor final.

Saint Louis es una ciudad independiente del estado de Misuri, Estados Unidos. Está ubicada sobre la orilla derecha del río Misisipi.

Dataset

Unidades: Millones de dólares, sin ajuste estacional

Frecuencia: mensual

Con base a una Encuesta de ventas minoristas mensual anticipada (Censo)

Cita: Oficina del Censo de EE. UU., Ventas minoristas anticipadas: Tiendas de ropa y accesorios de ropa [RSCCASN], obtenido de FRED, Banco de la Reserva Federal de St. Louis; https://fred.stlouisfed.org/series/RSCCASN, 26 de noviembre de 2020.

Limpieza de datos Ropa

C$DATE <- as.Date(C$DATE,format="%Y-%m-%d")

C$RSCCASN <- as.double(C$RSCCASN) 
C = rename(C, c(RSCCASN="Clothes", DATE='Date'))
C

Gráfica

p1 <- ggplot(data = C) + 
  geom_line(aes(x = Date, y = Clothes)) +
  ylab("Millones de dólares") + 
  xlab("Fecha") + ggtitle("Ventas de ropa minoristas EE.UU. ")
p1

Aquí de puede identificar un OUTLIER empezando el año 2020 causado por la contingencia del COVID-19.

Segunda Serie de Tiempo

Food <- read.csv('./RSAFSNA.csv')
Food

Teoría

Ventas minoristas anticipadas: Servicios minoristas y de alimentos

Las ventas minoristas Caracterizado por ser un negocio pequeño de origen familiar, se basa en la venta en menudeo o detallista es la empresa comercial o persona en régimen de autónomo que vende productos al consumidor final.

Saint Louis es una ciudad independiente del estado de Misuri, Estados Unidos. Está ubicada sobre la orilla derecha del río Misisipi.

Los trabajadores del comercio de alimentos han surgido como una nueva categoría de servicios de primera línea durante esta pandemia.

Dataset

Fuente: Publicación de la Oficina del Censo de EE. UU .: Ventas mensuales anticipadas para servicios minoristas y de alimentos

Unidades: Millones de dólares, sin ajuste estacional

Frecuencia: mensual

Con base a una Encuesta de ventas minoristas mensual anticipada (Censo)

Cita: U.S. Census Bureau, Advance Retail Sales: Retail and Food Services, Total [RSAFSNA], obtenido de FRED, Federal Reserve Bank of St. Louis; https://fred.stlouisfed.org/series/RSAFSNA, 26 de noviembre de 2020.

Limpieza de datos Comida

Food$DATE <- as.Date(Food$DATE,format="%Y-%m-%d")

Food$RSAFSNA <- as.double(Food$RSAFSNA) 
Food = rename(Food, c(RSAFSNA="Food", DATE='Date'))
Food

Gráfica

p2 <- ggplot(data = Food) + 
  geom_line(aes(x = Date, y = Food)) +
  ylab("Millones de dólares") + 
  xlab("Fecha") +
  ggtitle("Ventas de comida minoristas EE.UU. ")
p2

Graficar ambas

data <- merge (C, Food, by = "Date")
data
p3 <- ggplot(data = data) + 
  geom_line(aes(x = Date, y = Food)) +
  geom_line(aes(x = Date, y = Clothes)) +            
  ylab("Millones de dólares") + 
  xlab("Fecha")
p3

data1 <- data %>%
  select(Date,Clothes,Food)%>%
  mutate(date = yearmonth(Date))%>%
  as_tsibble(index = date)

data1
plotly::ggplotly(p3) 

Descomposición STL

Aplicamos la descomposición llamada ST (Seasonal & Trend Decomposition)

Aquí se lleva a cabo la descomposición de la serie, como se puede ver en la tabla. La tendencia (trend) muestra el movimiento de la serie, sin considerar las fluctuaciones estacionales ni el residuo. Podemos analizar la tendencia de la serie gráficamente:

Serie de Ropa


dcmp1 <- data1 %>%
  model(STL(Clothes))

components(dcmp1)


p1 <- data1 %>%
  autoplot(Clothes, color='gray') +
  autolayer(components(dcmp1), trend, color='red') +
  xlab("Fecha") + ylab("Millones de dólares") +
  ggtitle("Ventas de Ropa minoristas EE.UU. ")

dcmp2 <- data1 %>%
  model(STL(Food))

components(dcmp2)

p2 <- data1 %>%
  autoplot(Food, color='gray') +
  autolayer(components(dcmp2), trend, color='red') +
  xlab("Fecha") + ylab("Millones de dólares") +
  ggtitle("Ventas de comida minoristas EE.UU. ")

p1/p2

Graficamos los tres componentes simultáneamente. (Serie “Ropa”)

components(dcmp1) %>% autoplot() + xlab("Year")

Graficamos todos los componentes de la serie “Comida”

components(dcmp2) %>% autoplot() + xlab("Year")

Podemos hacer entonces un pronóstico de nuestra serie de tiempo usando decomposition_model().

data1 %>%
  model(stlf = decomposition_model(
             STL(Clothes ~ trend(window = 7), robust = TRUE),
             NAIVE(season_adjust)
  )) %>%
  forecast() %>%
  autoplot(data1)+ ylab("Millones de dólares") +
  ggtitle("Ropa ")

Serie de Comida.

data1 %>%
  model(stlf = decomposition_model(
             STL(Food ~ trend(window = 7), robust = TRUE),
             NAIVE(season_adjust)
  )) %>%
  forecast() %>%
  autoplot(data1)+ ylab("Millones de dólares") +
  ggtitle("Comida")

Pronósticos con los modelos básicos

Ropa

train <- data1 %>% filter_index("2010-01-01" ~ "2016-01-01")

fit <- train %>%
  model(
    Mean = MEAN(Clothes),
    `Naïve` = NAIVE(Clothes),
    `Seasonal naïve` = SNAIVE(Clothes),
    Drift = RW(Clothes ~ drift())
  )

fc <- fit %>%
  forecast(h = 80)

short <- data1 %>% filter_index("01-01-2010" ~ .)


fc %>%
  autoplot(short, level = NULL) +
  xlab("Año") + ylab("Millones de dólares") +
  ggtitle("Pronóstico para la venta minorista de ropa") +
  guides(colour=guide_legend(title="Forecast"))

fr <- data1 %>%
  filter_index('2000 Jan'~'2019 Jan')

adjusted <- fr %>%
  model(`Media`=MEAN(Clothes),
        `Naive`=NAIVE(Clothes),
        `Drift`=NAIVE(Clothes~drift()),
        `Seasonal naïve` = SNAIVE(Clothes))

fc <- adjusted %>%
  forecast(h=30)

fc %>%
  autoplot(short)+ ylab("Millones de dólares") +
  ggtitle("Ventas de ropa minorista")

accuracy(fit)

Se puede ver que el modelo de predicción que mejor se ajusta a esta serie en particular es el Seasonal Naive, pues es una serie altamente estacional.

Diagnóstico de residuales

aug <- augment(fit)
aug
train %>% 
  model(SNAIVE(Clothes)) %>% 
  gg_tsresiduals() + 
  ggtitle("Diagnóstico de residuales para el modelo Seasonal Naïve")

Diagnóstico de residuales

  • En la primera gráfica de los residuos no se percibe ningún patrón fuertemente marcado ni tendencia, sino que los residuos parecen aleatorios y la media de los datos se acerca a cero.

  • La gráfica de ACF muestra pocos rezagos significativos.

  • El histograma muestra una distribución normal.

Tests de Portmanteau de autocorrelación

aug %>% features(.resid, box_pierce, lag=2, dof=0)
aug %>% features(.resid, ljung_box, lag=2, dof=0)

Se puede observar que en todos los modelos, de Box-Pierce y de Ljung-Box, se descarta la hipótesis nula (de que no es ruido blanco) ya que los p-value son muy pequeños e indica que existe una autocorrelación.

Comida

train <- data1 %>% filter_index("2010-01-01" ~ "2018-01-01")

fit <- train %>%
  model(
    Mean = MEAN(Food),
    `Naïve` = NAIVE(Food),
    `Seasonal naïve` = SNAIVE(Food),
    Drift = RW(Food ~ drift())
  )

fc <- fit %>%
  forecast(h = 80)

fc %>%
  autoplot(short, level = NULL) +
  xlab("Año") + ylab("Millones de dólares") +
  ggtitle("Pronóstico para la venta minorista de comida") +
  guides(colour=guide_legend(title="Forecast"))

fr <- data1 %>%
  filter_index('2000 Jan'~'2019 Jan')

adjusted <- fr %>%
  model(`Media`=MEAN(Food),
        `Naive`=NAIVE(Food),
        `Drift`=NAIVE(Food~drift()),
        `Seasonal naïve` = SNAIVE(Food))

fc <- adjusted %>%
  forecast(h=30)

fc %>%
  autoplot(short)+ylab("Millones de dólares") +
  ggtitle("Ventas de comida minoristas ")

accuracy(fit)

También en este caso el modelo que mas se ajusta a la serie es el Seasonal Naive

Diagnóstico de residuales

aug2 <- augment(fit)
aug2
train %>% 
  model(SNAIVE(Food)) %>% 
  gg_tsresiduals() + 
  ggtitle("Diagnóstico de residuales para el modelo Seasonal Naïve")

Diagnóstico de residuales

  • En la primera gráfica de los residuos no se percibe ningún patrón fuertemente marcado ni tendencia, sino que los residuos parecen aleatorios y la media de los datos se acerca a cero.

  • La gráfica de ACF muestra pocos rezagos significativos.

  • El histograma muestra una distribución que se acerca a una normal.

Tests de Portmanteau de autocorrelación

aug2 %>% features(.resid, box_pierce, lag=2, dof=0)
aug2 %>% features(.resid, ljung_box, lag=2, dof=0)

Se puede observar que en todos los modelos, de Box-Pierce y de Ljung-Box, se descarta la hipótesis nula (de que no es ruido blanco) ya que los p-value son muy pequeños e indica que existe una autocorrelación.

Suavización Exponencial

Ropa

# Entrenamiento de los modelos
fit3 <- train %>%
  model(
    `Seasonal naïve` = SNAIVE(Clothes),
    `Damped Holt Winters` = ETS(Clothes ~ error("M") + trend("Ad") + 
                           season("M")),
    `ETS sin tendencia y aditivo` = ETS(Clothes ~ error("A") + trend("N") + season("A"))
  )

# Pronóstico
fc <- fit3 %>%
  forecast(h = 80)

a <- fc %>%
  autoplot(short, level = NULL) +
  xlab("Year") + ylab("Millones de dólares") +
  ggtitle("Suavización exponencial vs Métodos de Referencia Ropa") +
  guides(colour=guide_legend(title="Forecast"))

b <-  a + tidyquant::coord_x_date(xlim = c("2017-01-01","2022-01-01")) + ggtitle("") + 
  theme(legend.position = "none")

(a) / (b)

Errores de pronóstico Ropa

accuracy(fc, data1)
The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
47 observations are missing between 2020 nov. and 2024 sep.

El mejor modelo es el de Seasonal Naïve, pues tiene los valores menores de error de predicción en todas las métricas.

Comida

# Entrenamiento de los modelos
fit3 <- train %>%
  model(
    `Seasonal naïve` = SNAIVE(Food),
    `Damped Holt Winters` = ETS(Food ~ error("M") + trend("Ad") + 
                           season("M")),
    `ETS sin tendencia y aditivo` = ETS(Food ~ error("A") + trend("N") + season("A"))
  )

# Pronóstico
fc <- fit3 %>%
  forecast(h = 80)

a <- fc %>%
  autoplot(short, level = NULL) +
  xlab("Year") + ylab("Millones de dólares") +
  ggtitle("Suavización exponencial vs Métodos de Referencia Food") +
  guides(colour=guide_legend(title="Forecast"))

b <-  a + tidyquant::coord_x_date(xlim = c("2017-01-01","2022-01-01")) + ggtitle("") + 
  theme(legend.position = "none")

(a) / (b)

Errores de pronóstico Comida

accuracy(fc, data1)
The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
47 observations are missing between 2020 nov. and 2024 sep.

Aquí el mejor modelo es el Holt-Winters Amortiguado, por tener los errores de pronóstico menores.

food_tsibble <- data1 %>%
  select(date,Food)%>%
  as_tsibble(index = date)

food_tsibble

Modelo Arima

Ropa

fit <- fr %>%
  model(ARIMA(Clothes ~ PDQ(1,1,0),
              stepwise = FALSE, approximation = FALSE))
report(fit)
Series: Clothes 
Model: ARIMA(3,0,0)(1,1,0)[12] w/ drift 

Coefficients:
         ar1     ar2     ar3     sar1  constant
      0.1947  0.2255  0.3919  -0.1167   97.1027
s.e.  0.0621  0.0616  0.0631   0.0714   37.7071

sigma^2 estimated as 340362:  log likelihood=-1688.01
AIC=3388.02   AICc=3388.42   BIC=3408.3
fit <- fr %>%
  model(ARIMA(Clothes ~ pdq(2,1,0) + PDQ(1,1,0)),
        `Damped` = ETS(Clothes~error("A") + trend("Ad", phi = 0.9))
        )
rev <- fit %>%
  augment()

rev
accuracy(fit)

Gráfica SARIMA

fit_arima_prueba <- fr %>%
  model(ARIMA(Clothes ~ pdq(2,1,0) + PDQ(1,1,0)))

fc_arima_prueba <- fit_arima_prueba %>%
  forecast(h=25)
        
fc_arima_prueba %>% 
  autoplot(short) +
  ggtitle("Pronóstico Venta de ropa minorista 2019 y 2020") +
  xlab("Año") + ylab ("Millones de dólares")

Gráfica Damped

fit <- fr %>%
model(`Damped` = ETS(Clothes~error("A") + trend("Ad", phi = 0.9)))

fc <- fit %>%
  forecast(h=30)
fc %>%
  autoplot(short)+
  ggtitle("Pronóstico Venta de ropa minorista") +
  xlab("Año") + ylab ("Millones de dólares")

Comida

fit <- fr %>%
  model(ARIMA(Food ~ PDQ(1,1,0),
              stepwise = FALSE, approximation = FALSE))
report(fit)
Series: Food 
Model: ARIMA(4,0,1)(1,1,0)[12] w/ drift 

Coefficients:
         ar1      ar2    ar3      ar4      ma1     sar1  constant
      1.2364  -0.0230  0.097  -0.3349  -0.8607  -0.1930  351.6589
s.e.  0.1865   0.1413  0.132   0.0689   0.2046   0.0805   72.6405

sigma^2 estimated as 59476383:  log likelihood=-2247.75
AIC=4511.5   AICc=4512.19   BIC=4538.54
fit <- fr %>%
  model(ARIMA(Food ~ pdq(2,1,0) + PDQ(1,1,0)),
        `Damped` = ETS(Food~error("A") + trend("Ad", phi = 0.9))
        )
rev <- fit %>%
  augment()
rev
accuracy(fit)
NA

Gráfica SARIMA

fit_arima_prueba <- fr %>%
  model(ARIMA(Food ~ pdq(2,1,0) + PDQ(1,1,0)))

fc_arima_prueba <- fit_arima_prueba %>%
  forecast(h=25)
        
fc_arima_prueba %>% 
  autoplot(short) +
  ggtitle("Pronóstico Venta de comida minorista 2019 y 2020") +
  xlab("Año") + ylab ("Millones de dólares")

Gráfica Damped

fit <- fr %>%
model(`Damped` = ETS(Food~error("A") + trend("Ad", phi = 0.9)))

fc <- fit %>%
  forecast(h=30)
fc %>%
  autoplot(short)+
  ggtitle("Pronóstico Venta de comida minorista") +
  xlab("Año") + ylab ("Millones de dólares")

Se puede observar que el metodo Sarima se ajusta mejor a ambas series de tiempo.

Conclusión

  • De entre todos los modelos basicos de pronósticos para la serie de tiempo de las Ventas minoristas de tiendas de ropa y accesorios de ropa, el que mejor se ajustaba inicialmente es el Seasonal Naïve.

  • Para el metodo de suavizacion expponencial, el que mejor se ajustaba a los datos fue el modelo de Holt-Winters Amortiguado.

  • El método Damped y Arima terminan por ser mejores modelos para pronosticar, pero el método Arima Estacional es el mejor; tiene un mejor ajuste según los datos en ambas series de tiempo.

  • Por último podemos concluir que:

    • Para la serie de tiempo de la comida minorista: Modelo Holt Winters Amortiguado es muy bueno, pero el mejor es Arima estacional (Sarima) porque tiene los errores de pronóstico más pequeños.

    • Para la serie de tiempo de la venta de ropa: Damped fue muy buena, pero la mejor es Arima estacional (Sarima) porque tiene los errores de pronóstico más pequeños.

LS0tDQp0aXRsZTogIlByb3llY3RvIEZpbmFsOiBTZXJpZXMgZGUgVGllbXBvcyINCmF1dGhvcjogIkFuYSBNYXLDrWEgQWd1aWxlcmEiDQpkYXRlOiAyMDIwLTA0LTEyDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRoZW1lOiB1bml0ZWQNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGdpdGh1Yl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGRldjoganBlZw0KYWx3YXlzX2FsbG93X2h0bWw6IHRydWUNCi0tLQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoImVhc3lwYWNrYWdlcyIpDQpsaWJyYXJ5KHRzaWJibGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeXF1YW50KQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZnBwMikNCmxpYnJhcnkoZnBwMykNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KGZlYXN0cykNCmxpYnJhcnkoZmFibGUpDQpyZXF1aXJlKHJlc2hhcGUpDQoNCnBhY2thZ2VzKCJ0aWR5dmVyc2UiLCAidGlkeXF1YW50IiwgImx1YnJpZGF0ZSIsICJwYXRjaHdvcmsiLCAiZnBwMiIsImZwcDMiLCJzY2FsZXMiLCAidGltZXRrIikNCmBgYA0KDQoNCiMgUHJpbWVyIFNlcmllIGRlIFRpZW1wbw0KYGBge3J9DQpDIDwtIHJlYWQuY3N2KCcuL1JTQ0NBU04uY3N2JykNCkMNCg0KYGBgDQogDQojIyBUZW9yw61hDQoqKlZlbnRhcyBtaW5vcmlzdGFzIGFudGljaXBhZGFzOiBUaWVuZGFzIGRlIHJvcGEgeSBhY2Nlc29yaW9zIGRlIHJvcGEqKg0KDQpMYXMgdmVudGFzIG1pbm9yaXN0YXMNCkNhcmFjdGVyaXphZG8gcG9yIHNlciB1biBuZWdvY2lvIHBlcXVlw7FvIGRlIG9yaWdlbiBmYW1pbGlhciwgc2UgYmFzYSBlbiBsYSB2ZW50YSBlbiBtZW51ZGVvIG8gZGV0YWxsaXN0YSBlcyBsYSBlbXByZXNhIGNvbWVyY2lhbCBvIHBlcnNvbmEgZW4gcsOpZ2ltZW4gZGUgYXV0w7Nub21vIHF1ZSB2ZW5kZSBwcm9kdWN0b3MgYWwgY29uc3VtaWRvciBmaW5hbC4gDQoNClNhaW50IExvdWlzIGVzIHVuYSBjaXVkYWQgaW5kZXBlbmRpZW50ZSBkZWwgZXN0YWRvIGRlIE1pc3VyaSwgRXN0YWRvcyBVbmlkb3MuIEVzdMOhIHViaWNhZGEgc29icmUgbGEgb3JpbGxhIGRlcmVjaGEgZGVsIHLDrW8gTWlzaXNpcGkuDQoNCg0KKipEYXRhc2V0KioNCg0KVW5pZGFkZXM6IE1pbGxvbmVzIGRlIGTDs2xhcmVzLCBzaW4gYWp1c3RlIGVzdGFjaW9uYWwNCg0KRnJlY3VlbmNpYTogbWVuc3VhbA0KDQpDb24gYmFzZSBhIHVuYSBFbmN1ZXN0YSBkZSB2ZW50YXMgbWlub3Jpc3RhcyBtZW5zdWFsIGFudGljaXBhZGEgKENlbnNvKQ0KDQpDaXRhOg0KT2ZpY2luYSBkZWwgQ2Vuc28gZGUgRUUuIFVVLiwgVmVudGFzIG1pbm9yaXN0YXMgYW50aWNpcGFkYXM6IFRpZW5kYXMgZGUgcm9wYSB5IGFjY2Vzb3Jpb3MgZGUgcm9wYSBbUlNDQ0FTTl0sIG9idGVuaWRvIGRlIEZSRUQsIEJhbmNvIGRlIGxhIFJlc2VydmEgRmVkZXJhbCBkZSBTdC4gTG91aXM7IGh0dHBzOi8vZnJlZC5zdGxvdWlzZmVkLm9yZy9zZXJpZXMvUlNDQ0FTTiwgMjYgZGUgbm92aWVtYnJlIGRlIDIwMjAuDQoNCg0KDQojIyBMaW1waWV6YSBkZSBkYXRvcyBSb3BhDQpgYGB7cn0NCkMkREFURSA8LSBhcy5EYXRlKEMkREFURSxmb3JtYXQ9IiVZLSVtLSVkIikNCg0KQyRSU0NDQVNOIDwtIGFzLmRvdWJsZShDJFJTQ0NBU04pIA0KYGBgDQoNCmBgYHtyfQ0KQyA9IHJlbmFtZShDLCBjKFJTQ0NBU049IkNsb3RoZXMiLCBEQVRFPSdEYXRlJykpDQpDDQpgYGANCg0KIyMgR3LDoWZpY2ENCmBgYHtyfQ0KcDEgPC0gZ2dwbG90KGRhdGEgPSBDKSArIA0KICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gQ2xvdGhlcykpICsNCiAgeWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArIA0KICB4bGFiKCJGZWNoYSIpICsgZ2d0aXRsZSgiVmVudGFzIGRlIHJvcGEgbWlub3Jpc3RhcyBFRS5VVS4gIikNCnAxDQpgYGANCkFxdcOtIGRlIHB1ZWRlIGlkZW50aWZpY2FyIHVuIE9VVExJRVIgZW1wZXphbmRvIGVsIGHDsW8gMjAyMCBjYXVzYWRvIHBvciBsYSBjb250aW5nZW5jaWEgZGVsIENPVklELTE5Lg0KDQoNCiMgU2VndW5kYSBTZXJpZSBkZSBUaWVtcG8NCmBgYHtyfQ0KRm9vZCA8LSByZWFkLmNzdignLi9SU0FGU05BLmNzdicpDQpGb29kDQpgYGANCg0KIyMgVGVvcsOtYQ0KKipWZW50YXMgbWlub3Jpc3RhcyBhbnRpY2lwYWRhczogU2VydmljaW9zIG1pbm9yaXN0YXMgeSBkZSBhbGltZW50b3MqKg0KDQpMYXMgdmVudGFzIG1pbm9yaXN0YXMNCkNhcmFjdGVyaXphZG8gcG9yIHNlciB1biBuZWdvY2lvIHBlcXVlw7FvIGRlIG9yaWdlbiBmYW1pbGlhciwgc2UgYmFzYSBlbiBsYSB2ZW50YSBlbiBtZW51ZGVvIG8gZGV0YWxsaXN0YSBlcyBsYSBlbXByZXNhIGNvbWVyY2lhbCBvIHBlcnNvbmEgZW4gcsOpZ2ltZW4gZGUgYXV0w7Nub21vIHF1ZSB2ZW5kZSBwcm9kdWN0b3MgYWwgY29uc3VtaWRvciBmaW5hbC4gDQoNClNhaW50IExvdWlzIGVzIHVuYSBjaXVkYWQgaW5kZXBlbmRpZW50ZSBkZWwgZXN0YWRvIGRlIE1pc3VyaSwgRXN0YWRvcyBVbmlkb3MuIEVzdMOhIHViaWNhZGEgc29icmUgbGEgb3JpbGxhIGRlcmVjaGEgZGVsIHLDrW8gTWlzaXNpcGkuDQoNCkxvcyB0cmFiYWphZG9yZXMgZGVsIGNvbWVyY2lvIGRlIGFsaW1lbnRvcyBoYW4gc3VyZ2lkbyBjb21vIHVuYSBudWV2YSBjYXRlZ29yw61hIGRlIHNlcnZpY2lvcyBkZSBwcmltZXJhIGzDrW5lYSBkdXJhbnRlIGVzdGEgcGFuZGVtaWEuIA0KDQoqKkRhdGFzZXQqKg0KDQpGdWVudGU6IFB1YmxpY2FjacOzbiBkZSBsYSBPZmljaW5hIGRlbCBDZW5zbyBkZSBFRS4gVVUgLjogVmVudGFzIG1lbnN1YWxlcyBhbnRpY2lwYWRhcyBwYXJhIHNlcnZpY2lvcyBtaW5vcmlzdGFzIHkgZGUgYWxpbWVudG9zDQoNClVuaWRhZGVzOiBNaWxsb25lcyBkZSBkw7NsYXJlcywgc2luIGFqdXN0ZSBlc3RhY2lvbmFsDQoNCkZyZWN1ZW5jaWE6IG1lbnN1YWwNCg0KDQpDb24gYmFzZSBhIHVuYSBFbmN1ZXN0YSBkZSB2ZW50YXMgbWlub3Jpc3RhcyBtZW5zdWFsIGFudGljaXBhZGEgKENlbnNvKQ0KDQpDaXRhOg0KVS5TLiBDZW5zdXMgQnVyZWF1LCBBZHZhbmNlIFJldGFpbCBTYWxlczogUmV0YWlsIGFuZCBGb29kIFNlcnZpY2VzLCBUb3RhbCBbUlNBRlNOQV0sIG9idGVuaWRvIGRlIEZSRUQsIEZlZGVyYWwgUmVzZXJ2ZSBCYW5rIG9mIFN0LiBMb3VpczsgaHR0cHM6Ly9mcmVkLnN0bG91aXNmZWQub3JnL3Nlcmllcy9SU0FGU05BLCAyNiBkZSBub3ZpZW1icmUgZGUgMjAyMC4NCg0KDQojIyBMaW1waWV6YSBkZSBkYXRvcyBDb21pZGENCmBgYHtyfQ0KRm9vZCREQVRFIDwtIGFzLkRhdGUoRm9vZCREQVRFLGZvcm1hdD0iJVktJW0tJWQiKQ0KDQpGb29kJFJTQUZTTkEgPC0gYXMuZG91YmxlKEZvb2QkUlNBRlNOQSkgDQpgYGANCg0KYGBge3J9DQpGb29kID0gcmVuYW1lKEZvb2QsIGMoUlNBRlNOQT0iRm9vZCIsIERBVEU9J0RhdGUnKSkNCkZvb2QNCmBgYA0KDQoNCiMjIEdyw6FmaWNhDQpgYGB7cn0NCnAyIDwtIGdncGxvdChkYXRhID0gRm9vZCkgKyANCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IEZvb2QpKSArDQogIHlsYWIoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikgKyANCiAgeGxhYigiRmVjaGEiKSArDQogIGdndGl0bGUoIlZlbnRhcyBkZSBjb21pZGEgbWlub3Jpc3RhcyBFRS5VVS4gIikNCnAyDQpgYGANCiMjIEdyYWZpY2FyIGFtYmFzDQpgYGB7cn0NCmRhdGEgPC0gbWVyZ2UgKEMsIEZvb2QsIGJ5ID0gIkRhdGUiKQ0KZGF0YQ0KYGBgDQoNCg0KYGBge3J9DQpwMyA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEpICsgDQogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBGb29kKSkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gQ2xvdGhlcykpICsgICAgICAgICAgICANCiAgeWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArIA0KICB4bGFiKCJGZWNoYSIpDQpwMw0KYGBgDQoNCg0KYGBge3J9DQpkYXRhMSA8LSBkYXRhICU+JQ0KICBzZWxlY3QoRGF0ZSxDbG90aGVzLEZvb2QpJT4lDQogIG11dGF0ZShkYXRlID0geWVhcm1vbnRoKERhdGUpKSU+JQ0KICBhc190c2liYmxlKGluZGV4ID0gZGF0ZSkNCg0KZGF0YTENCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90bHk6OmdncGxvdGx5KHAzKSANCmBgYA0KDQoNCiMgRGVzY29tcG9zaWNpw7NuIFNUTA0KDQpBcGxpY2Ftb3MgbGEgZGVzY29tcG9zaWNpw7NuIGxsYW1hZGEgU1QgKFNlYXNvbmFsICYgVHJlbmQgRGVjb21wb3NpdGlvbikNCg0KQXF1w60gc2UgbGxldmEgYSBjYWJvIGxhIGRlc2NvbXBvc2ljacOzbiBkZSBsYSBzZXJpZSwgY29tbyBzZSBwdWVkZSB2ZXIgZW4gbGEgdGFibGEuIExhIHRlbmRlbmNpYSAodHJlbmQpIG11ZXN0cmEgZWwgbW92aW1pZW50byBkZSBsYSBzZXJpZSwgc2luIGNvbnNpZGVyYXIgbGFzIGZsdWN0dWFjaW9uZXMgZXN0YWNpb25hbGVzIG5pIGVsIHJlc2lkdW8uIFBvZGVtb3MgYW5hbGl6YXIgbGEgdGVuZGVuY2lhIGRlIGxhIHNlcmllIGdyw6FmaWNhbWVudGU6DQoNCiMjIFNlcmllIGRlIFJvcGENCmBgYHtyfQ0KDQpkY21wMSA8LSBkYXRhMSAlPiUNCiAgbW9kZWwoU1RMKENsb3RoZXMpKQ0KDQpjb21wb25lbnRzKGRjbXAxKQ0KDQoNCnAxIDwtIGRhdGExICU+JQ0KICBhdXRvcGxvdChDbG90aGVzLCBjb2xvcj0nZ3JheScpICsNCiAgYXV0b2xheWVyKGNvbXBvbmVudHMoZGNtcDEpLCB0cmVuZCwgY29sb3I9J3JlZCcpICsNCiAgeGxhYigiRmVjaGEiKSArIHlsYWIoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikgKw0KICBnZ3RpdGxlKCJWZW50YXMgZGUgUm9wYSBtaW5vcmlzdGFzIEVFLlVVLiAiKQ0KDQpkY21wMiA8LSBkYXRhMSAlPiUNCiAgbW9kZWwoU1RMKEZvb2QpKQ0KDQpjb21wb25lbnRzKGRjbXAyKQ0KDQpwMiA8LSBkYXRhMSAlPiUNCiAgYXV0b3Bsb3QoRm9vZCwgY29sb3I9J2dyYXknKSArDQogIGF1dG9sYXllcihjb21wb25lbnRzKGRjbXAyKSwgdHJlbmQsIGNvbG9yPSdyZWQnKSArDQogIHhsYWIoIkZlY2hhIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiVmVudGFzIGRlIGNvbWlkYSBtaW5vcmlzdGFzIEVFLlVVLiAiKQ0KDQpwMS9wMg0KYGBgDQoNCkdyYWZpY2Ftb3MgbG9zIHRyZXMgY29tcG9uZW50ZXMgc2ltdWx0w6FuZWFtZW50ZS4gKFNlcmllICJSb3BhIikNCg0KYGBge3J9DQpjb21wb25lbnRzKGRjbXAxKSAlPiUgYXV0b3Bsb3QoKSArIHhsYWIoIlllYXIiKQ0KYGBgDQoNCkdyYWZpY2Ftb3MgdG9kb3MgbG9zIGNvbXBvbmVudGVzIGRlIGxhIHNlcmllICJDb21pZGEiDQoNCmBgYHtyfQ0KY29tcG9uZW50cyhkY21wMikgJT4lIGF1dG9wbG90KCkgKyB4bGFiKCJZZWFyIikNCmBgYA0KUG9kZW1vcyBoYWNlciBlbnRvbmNlcyB1biBwcm9uw7NzdGljbyBkZSBudWVzdHJhIHNlcmllIGRlIHRpZW1wbyB1c2FuZG8gZGVjb21wb3NpdGlvbl9tb2RlbCgpLg0KDQpgYGB7cn0NCmRhdGExICU+JQ0KICBtb2RlbChzdGxmID0gZGVjb21wb3NpdGlvbl9tb2RlbCgNCiAgICAgICAgICAgICBTVEwoQ2xvdGhlcyB+IHRyZW5kKHdpbmRvdyA9IDcpLCByb2J1c3QgPSBUUlVFKSwNCiAgICAgICAgICAgICBOQUlWRShzZWFzb25fYWRqdXN0KQ0KICApKSAlPiUNCiAgZm9yZWNhc3QoKSAlPiUNCiAgYXV0b3Bsb3QoZGF0YTEpKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiUm9wYSAiKQ0KYGBgDQoNCg0KIyMgU2VyaWUgZGUgQ29taWRhLg0KDQpgYGB7cn0NCmRhdGExICU+JQ0KICBtb2RlbChzdGxmID0gZGVjb21wb3NpdGlvbl9tb2RlbCgNCiAgICAgICAgICAgICBTVEwoRm9vZCB+IHRyZW5kKHdpbmRvdyA9IDcpLCByb2J1c3QgPSBUUlVFKSwNCiAgICAgICAgICAgICBOQUlWRShzZWFzb25fYWRqdXN0KQ0KICApKSAlPiUNCiAgZm9yZWNhc3QoKSAlPiUNCiAgYXV0b3Bsb3QoZGF0YTEpKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiQ29taWRhIikNCmBgYA0KDQojIFByb27Ds3N0aWNvcyBjb24gbG9zIG1vZGVsb3MgYsOhc2ljb3MNCg0KIyMgUm9wYQ0KYGBge3J9DQp0cmFpbiA8LSBkYXRhMSAlPiUgZmlsdGVyX2luZGV4KCIyMDEwLTAxLTAxIiB+ICIyMDE2LTAxLTAxIikNCg0KZml0IDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBNZWFuID0gTUVBTihDbG90aGVzKSwNCiAgICBgTmHDr3ZlYCA9IE5BSVZFKENsb3RoZXMpLA0KICAgIGBTZWFzb25hbCBuYcOvdmVgID0gU05BSVZFKENsb3RoZXMpLA0KICAgIERyaWZ0ID0gUlcoQ2xvdGhlcyB+IGRyaWZ0KCkpDQogICkNCg0KZmMgPC0gZml0ICU+JQ0KICBmb3JlY2FzdChoID0gODApDQoNCnNob3J0IDwtIGRhdGExICU+JSBmaWx0ZXJfaW5kZXgoIjAxLTAxLTIwMTAiIH4gLikNCg0KDQpmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQsIGxldmVsID0gTlVMTCkgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiUHJvbsOzc3RpY28gcGFyYSBsYSB2ZW50YSBtaW5vcmlzdGEgZGUgcm9wYSIpICsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkZvcmVjYXN0IikpDQpgYGANCg0KDQpgYGB7cn0NCmZyIDwtIGRhdGExICU+JQ0KICBmaWx0ZXJfaW5kZXgoJzIwMDAgSmFuJ34nMjAxOSBKYW4nKQ0KDQphZGp1c3RlZCA8LSBmciAlPiUNCiAgbW9kZWwoYE1lZGlhYD1NRUFOKENsb3RoZXMpLA0KICAgICAgICBgTmFpdmVgPU5BSVZFKENsb3RoZXMpLA0KICAgICAgICBgRHJpZnRgPU5BSVZFKENsb3RoZXN+ZHJpZnQoKSksDQogICAgICAgIGBTZWFzb25hbCBuYcOvdmVgID0gU05BSVZFKENsb3RoZXMpKQ0KDQpmYyA8LSBhZGp1c3RlZCAlPiUNCiAgZm9yZWNhc3QoaD0zMCkNCg0KZmMgJT4lDQogIGF1dG9wbG90KHNob3J0KSsgeWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArDQogIGdndGl0bGUoIlZlbnRhcyBkZSByb3BhIG1pbm9yaXN0YSIpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KYWNjdXJhY3koZml0KQ0KYGBgDQpTZSBwdWVkZSB2ZXIgcXVlIGVsIG1vZGVsbyBkZSBwcmVkaWNjacOzbiBxdWUgbWVqb3Igc2UgYWp1c3RhIGEgZXN0YSBzZXJpZSBlbiBwYXJ0aWN1bGFyIGVzIGVsIFNlYXNvbmFsIE5haXZlLCBwdWVzIGVzIHVuYSBzZXJpZSBhbHRhbWVudGUgZXN0YWNpb25hbC4NCg0KIyMgRGlhZ27Ds3N0aWNvIGRlIHJlc2lkdWFsZXMNCg0KYGBge3J9DQphdWcgPC0gYXVnbWVudChmaXQpDQphdWcNCmBgYA0KDQoNCg0KYGBge3Igd2FybmluZyA9IEZ9DQp0cmFpbiAlPiUgDQogIG1vZGVsKFNOQUlWRShDbG90aGVzKSkgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpICsgDQogIGdndGl0bGUoIkRpYWduw7NzdGljbyBkZSByZXNpZHVhbGVzIHBhcmEgZWwgbW9kZWxvIFNlYXNvbmFsIE5hw692ZSIpDQpgYGANCiMjIERpYWduw7NzdGljbyBkZSByZXNpZHVhbGVzDQoNCiogRW4gbGEgcHJpbWVyYSBncsOhZmljYSBkZSBsb3MgcmVzaWR1b3Mgbm8gc2UgcGVyY2liZSBuaW5nw7puIHBhdHLDs24gZnVlcnRlbWVudGUgbWFyY2FkbyBuaSB0ZW5kZW5jaWEsIHNpbm8gcXVlIGxvcyByZXNpZHVvcyBwYXJlY2VuIGFsZWF0b3Jpb3MgeSBsYSBtZWRpYSBkZSBsb3MgZGF0b3Mgc2UgYWNlcmNhIGEgY2Vyby4NCg0KKiBMYSBncsOhZmljYSBkZSBBQ0YgbXVlc3RyYSBwb2NvcyByZXphZ29zIHNpZ25pZmljYXRpdm9zLg0KDQoqIEVsIGhpc3RvZ3JhbWEgbXVlc3RyYSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuDQoNCg0KIyMgVGVzdHMgZGUgUG9ydG1hbnRlYXUgZGUgYXV0b2NvcnJlbGFjacOzbg0KDQpgYGB7cn0NCmF1ZyAlPiUgZmVhdHVyZXMoLnJlc2lkLCBib3hfcGllcmNlLCBsYWc9MiwgZG9mPTApDQpgYGANCg0KYGBge3J9DQphdWcgJT4lIGZlYXR1cmVzKC5yZXNpZCwgbGp1bmdfYm94LCBsYWc9MiwgZG9mPTApDQpgYGANCg0KU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVuIHRvZG9zIGxvcyBtb2RlbG9zLCBkZSBCb3gtUGllcmNlIHkgZGUgTGp1bmctQm94LCBzZSBkZXNjYXJ0YSBsYSBoaXDDs3Rlc2lzIG51bGEgKGRlIHF1ZSBubyBlcyBydWlkbyBibGFuY28pIHlhIHF1ZSBsb3MgcC12YWx1ZSBzb24gbXV5IHBlcXVlw7FvcyBlIGluZGljYSBxdWUgZXhpc3RlIHVuYSBhdXRvY29ycmVsYWNpw7NuLg0KDQoNCiMjIENvbWlkYQ0KYGBge3J9DQp0cmFpbiA8LSBkYXRhMSAlPiUgZmlsdGVyX2luZGV4KCIyMDEwLTAxLTAxIiB+ICIyMDE4LTAxLTAxIikNCg0KZml0IDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBNZWFuID0gTUVBTihGb29kKSwNCiAgICBgTmHDr3ZlYCA9IE5BSVZFKEZvb2QpLA0KICAgIGBTZWFzb25hbCBuYcOvdmVgID0gU05BSVZFKEZvb2QpLA0KICAgIERyaWZ0ID0gUlcoRm9vZCB+IGRyaWZ0KCkpDQogICkNCg0KZmMgPC0gZml0ICU+JQ0KICBmb3JlY2FzdChoID0gODApDQoNCmZjICU+JQ0KICBhdXRvcGxvdChzaG9ydCwgbGV2ZWwgPSBOVUxMKSArDQogIHhsYWIoIkHDsW8iKSArIHlsYWIoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikgKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBwYXJhIGxhIHZlbnRhIG1pbm9yaXN0YSBkZSBjb21pZGEiKSArDQogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJGb3JlY2FzdCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpmciA8LSBkYXRhMSAlPiUNCiAgZmlsdGVyX2luZGV4KCcyMDAwIEphbid+JzIwMTkgSmFuJykNCg0KYWRqdXN0ZWQgPC0gZnIgJT4lDQogIG1vZGVsKGBNZWRpYWA9TUVBTihGb29kKSwNCiAgICAgICAgYE5haXZlYD1OQUlWRShGb29kKSwNCiAgICAgICAgYERyaWZ0YD1OQUlWRShGb29kfmRyaWZ0KCkpLA0KICAgICAgICBgU2Vhc29uYWwgbmHDr3ZlYCA9IFNOQUlWRShGb29kKSkNCg0KZmMgPC0gYWRqdXN0ZWQgJT4lDQogIGZvcmVjYXN0KGg9MzApDQoNCmZjICU+JQ0KICBhdXRvcGxvdChzaG9ydCkreWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArDQogIGdndGl0bGUoIlZlbnRhcyBkZSBjb21pZGEgbWlub3Jpc3RhcyAiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmFjY3VyYWN5KGZpdCkNCmBgYA0KVGFtYmnDqW4gZW4gZXN0ZSBjYXNvIGVsIG1vZGVsbyBxdWUgbWFzIHNlIGFqdXN0YSBhIGxhIHNlcmllIGVzIGVsIFNlYXNvbmFsIE5haXZlDQoNCg0KIyMgRGlhZ27Ds3N0aWNvIGRlIHJlc2lkdWFsZXMNCg0KYGBge3J9DQphdWcyIDwtIGF1Z21lbnQoZml0KQ0KYXVnMg0KYGBgDQoNCg0KYGBge3Igd2FybmluZyA9IEZ9DQp0cmFpbiAlPiUgDQogIG1vZGVsKFNOQUlWRShGb29kKSkgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpICsgDQogIGdndGl0bGUoIkRpYWduw7NzdGljbyBkZSByZXNpZHVhbGVzIHBhcmEgZWwgbW9kZWxvIFNlYXNvbmFsIE5hw692ZSIpDQpgYGANCg0KIyMgRGlhZ27Ds3N0aWNvIGRlIHJlc2lkdWFsZXMNCg0KKiBFbiBsYSBwcmltZXJhIGdyw6FmaWNhIGRlIGxvcyByZXNpZHVvcyBubyBzZSBwZXJjaWJlIG5pbmfDum4gcGF0csOzbiBmdWVydGVtZW50ZSBtYXJjYWRvIG5pIHRlbmRlbmNpYSwgc2lubyBxdWUgbG9zIHJlc2lkdW9zIHBhcmVjZW4gYWxlYXRvcmlvcyB5IGxhIG1lZGlhIGRlIGxvcyBkYXRvcyBzZSBhY2VyY2EgYSBjZXJvLg0KDQoqIExhIGdyw6FmaWNhIGRlIEFDRiBtdWVzdHJhIHBvY29zIHJlemFnb3Mgc2lnbmlmaWNhdGl2b3MuDQoNCiogRWwgaGlzdG9ncmFtYSBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIHF1ZSBzZSBhY2VyY2EgYSB1bmEgbm9ybWFsLg0KDQoNCiMjIFRlc3RzIGRlIFBvcnRtYW50ZWF1IGRlIGF1dG9jb3JyZWxhY2nDs24NCg0KYGBge3J9DQphdWcyICU+JSBmZWF0dXJlcygucmVzaWQsIGJveF9waWVyY2UsIGxhZz0yLCBkb2Y9MCkNCmBgYA0KDQpgYGB7cn0NCmF1ZzIgJT4lIGZlYXR1cmVzKC5yZXNpZCwgbGp1bmdfYm94LCBsYWc9MiwgZG9mPTApDQpgYGANCg0KU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVuIHRvZG9zIGxvcyBtb2RlbG9zLCBkZSBCb3gtUGllcmNlIHkgZGUgTGp1bmctQm94LCBzZSBkZXNjYXJ0YSBsYSBoaXDDs3Rlc2lzIG51bGEgKGRlIHF1ZSBubyBlcyBydWlkbyBibGFuY28pIHlhIHF1ZSBsb3MgcC12YWx1ZSBzb24gbXV5IHBlcXVlw7FvcyBlIGluZGljYSBxdWUgZXhpc3RlIHVuYSBhdXRvY29ycmVsYWNpw7NuLg0KDQoNCiMgU3Vhdml6YWNpw7NuIEV4cG9uZW5jaWFsDQoNCiMjIFJvcGENCmBgYHtyfQ0KIyBFbnRyZW5hbWllbnRvIGRlIGxvcyBtb2RlbG9zDQpmaXQzIDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBgU2Vhc29uYWwgbmHDr3ZlYCA9IFNOQUlWRShDbG90aGVzKSwNCiAgICBgRGFtcGVkIEhvbHQgV2ludGVyc2AgPSBFVFMoQ2xvdGhlcyB+IGVycm9yKCJNIikgKyB0cmVuZCgiQWQiKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uKCJNIikpLA0KICAgIGBFVFMgc2luIHRlbmRlbmNpYSB5IGFkaXRpdm9gID0gRVRTKENsb3RoZXMgfiBlcnJvcigiQSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiQSIpKQ0KICApDQoNCiMgUHJvbsOzc3RpY28NCmZjIDwtIGZpdDMgJT4lDQogIGZvcmVjYXN0KGggPSA4MCkNCg0KYSA8LSBmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQsIGxldmVsID0gTlVMTCkgKw0KICB4bGFiKCJZZWFyIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiU3Vhdml6YWNpw7NuIGV4cG9uZW5jaWFsIHZzIE3DqXRvZG9zIGRlIFJlZmVyZW5jaWEgUm9wYSIpICsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkZvcmVjYXN0IikpDQoNCmIgPC0gIGEgKyB0aWR5cXVhbnQ6OmNvb3JkX3hfZGF0ZSh4bGltID0gYygiMjAxNy0wMS0wMSIsIjIwMjItMDEtMDEiKSkgKyBnZ3RpdGxlKCIiKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCihhKSAvIChiKQ0KDQpgYGANCg0KIyMgRXJyb3JlcyBkZSBwcm9uw7NzdGljbyBSb3BhDQpgYGB7cn0NCmFjY3VyYWN5KGZjLCBkYXRhMSkNCg0KYGBgDQoNCkVsIG1lam9yIG1vZGVsbyBlcyBlbCBkZSBTZWFzb25hbCBOYcOvdmUsIHB1ZXMgdGllbmUgbG9zIHZhbG9yZXMgbWVub3JlcyBkZSBlcnJvciBkZSBwcmVkaWNjacOzbiBlbiB0b2RhcyBsYXMgbcOpdHJpY2FzLg0KDQojIyBDb21pZGENCmBgYHtyfQ0KIyBFbnRyZW5hbWllbnRvIGRlIGxvcyBtb2RlbG9zDQpmaXQzIDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBgU2Vhc29uYWwgbmHDr3ZlYCA9IFNOQUlWRShGb29kKSwNCiAgICBgRGFtcGVkIEhvbHQgV2ludGVyc2AgPSBFVFMoRm9vZCB+IGVycm9yKCJNIikgKyB0cmVuZCgiQWQiKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uKCJNIikpLA0KICAgIGBFVFMgc2luIHRlbmRlbmNpYSB5IGFkaXRpdm9gID0gRVRTKEZvb2QgfiBlcnJvcigiQSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiQSIpKQ0KICApDQoNCiMgUHJvbsOzc3RpY28NCmZjIDwtIGZpdDMgJT4lDQogIGZvcmVjYXN0KGggPSA4MCkNCg0KYSA8LSBmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQsIGxldmVsID0gTlVMTCkgKw0KICB4bGFiKCJZZWFyIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiU3Vhdml6YWNpw7NuIGV4cG9uZW5jaWFsIHZzIE3DqXRvZG9zIGRlIFJlZmVyZW5jaWEgRm9vZCIpICsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkZvcmVjYXN0IikpDQoNCmIgPC0gIGEgKyB0aWR5cXVhbnQ6OmNvb3JkX3hfZGF0ZSh4bGltID0gYygiMjAxNy0wMS0wMSIsIjIwMjItMDEtMDEiKSkgKyBnZ3RpdGxlKCIiKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCihhKSAvIChiKQ0KDQpgYGANCg0KIyMgRXJyb3JlcyBkZSBwcm9uw7NzdGljbyBDb21pZGENCmBgYHtyfQ0KYWNjdXJhY3koZmMsIGRhdGExKQ0KDQpgYGANCkFxdcOtIGVsIG1lam9yIG1vZGVsbyBlcyBlbCBIb2x0LVdpbnRlcnMgQW1vcnRpZ3VhZG8sIHBvciB0ZW5lciBsb3MgZXJyb3JlcyBkZSBwcm9uw7NzdGljbyBtZW5vcmVzLg0KDQoNCmBgYHtyfQ0KZm9vZF90c2liYmxlIDwtIGRhdGExICU+JQ0KICBzZWxlY3QoZGF0ZSxGb29kKSU+JQ0KICBhc190c2liYmxlKGluZGV4ID0gZGF0ZSkNCg0KZm9vZF90c2liYmxlDQpgYGANCg0KIyBNb2RlbG8gQXJpbWENCg0KIyMgUm9wYQ0KYGBge3J9DQpmaXQgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKENsb3RoZXMgfiBQRFEoMSwxLDApLA0KICAgICAgICAgICAgICBzdGVwd2lzZSA9IEZBTFNFLCBhcHByb3hpbWF0aW9uID0gRkFMU0UpKQ0KcmVwb3J0KGZpdCkNCmBgYA0KDQpgYGB7cn0NCmZpdCA8LSBmciAlPiUNCiAgbW9kZWwoQVJJTUEoQ2xvdGhlcyB+IHBkcSgyLDEsMCkgKyBQRFEoMSwxLDApKSwNCiAgICAgICAgYERhbXBlZGAgPSBFVFMoQ2xvdGhlc35lcnJvcigiQSIpICsgdHJlbmQoIkFkIiwgcGhpID0gMC45KSkNCiAgICAgICAgKQ0KcmV2IDwtIGZpdCAlPiUNCiAgYXVnbWVudCgpDQoNCnJldg0KYGBgDQoNCg0KYGBge3J9DQphY2N1cmFjeShmaXQpDQpgYGANCg0KIyMgR3LDoWZpY2EgU0FSSU1BDQpgYGB7cn0NCmZpdF9hcmltYV9wcnVlYmEgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKENsb3RoZXMgfiBwZHEoMiwxLDApICsgUERRKDEsMSwwKSkpDQoNCmZjX2FyaW1hX3BydWViYSA8LSBmaXRfYXJpbWFfcHJ1ZWJhICU+JQ0KICBmb3JlY2FzdChoPTI1KQ0KICAgICAgICANCmZjX2FyaW1hX3BydWViYSAlPiUgDQogIGF1dG9wbG90KHNob3J0KSArDQogIGdndGl0bGUoIlByb27Ds3N0aWNvIFZlbnRhIGRlIHJvcGEgbWlub3Jpc3RhIDIwMTkgeSAyMDIwIikgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiICgiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpgYGANCg0KIyMgR3LDoWZpY2EgRGFtcGVkDQpgYGB7cn0NCmZpdCA8LSBmciAlPiUNCm1vZGVsKGBEYW1wZWRgID0gRVRTKENsb3RoZXN+ZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIsIHBoaSA9IDAuOSkpKQ0KDQpmYyA8LSBmaXQgJT4lDQogIGZvcmVjYXN0KGg9MzApDQpmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQpKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBWZW50YSBkZSByb3BhIG1pbm9yaXN0YSIpICsNCiAgeGxhYigiQcOxbyIpICsgeWxhYiAoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikNCg0KYGBgDQoNCiMjIENvbWlkYQ0KYGBge3J9DQpmaXQgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKEZvb2QgfiBQRFEoMSwxLDApLA0KICAgICAgICAgICAgICBzdGVwd2lzZSA9IEZBTFNFLCBhcHByb3hpbWF0aW9uID0gRkFMU0UpKQ0KcmVwb3J0KGZpdCkNCg0KYGBgDQoNCg0KYGBge3J9DQpmaXQgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKEZvb2QgfiBwZHEoMiwxLDApICsgUERRKDEsMSwwKSksDQogICAgICAgIGBEYW1wZWRgID0gRVRTKEZvb2R+ZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIsIHBoaSA9IDAuOSkpDQogICAgICAgICkNCnJldiA8LSBmaXQgJT4lDQogIGF1Z21lbnQoKQ0KcmV2DQpgYGANCg0KDQpgYGB7cn0NCmFjY3VyYWN5KGZpdCkNCg0KYGBgDQoNCiMjIEdyw6FmaWNhIFNBUklNQQ0KYGBge3J9DQpmaXRfYXJpbWFfcHJ1ZWJhIDwtIGZyICU+JQ0KICBtb2RlbChBUklNQShGb29kIH4gcGRxKDIsMSwwKSArIFBEUSgxLDEsMCkpKQ0KDQpmY19hcmltYV9wcnVlYmEgPC0gZml0X2FyaW1hX3BydWViYSAlPiUNCiAgZm9yZWNhc3QoaD0yNSkNCiAgICAgICAgDQpmY19hcmltYV9wcnVlYmEgJT4lIA0KICBhdXRvcGxvdChzaG9ydCkgKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBWZW50YSBkZSBjb21pZGEgbWlub3Jpc3RhIDIwMTkgeSAyMDIwIikgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiICgiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpgYGANCg0KIyMgR3LDoWZpY2EgRGFtcGVkDQpgYGB7cn0NCmZpdCA8LSBmciAlPiUNCm1vZGVsKGBEYW1wZWRgID0gRVRTKEZvb2R+ZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIsIHBoaSA9IDAuOSkpKQ0KDQpmYyA8LSBmaXQgJT4lDQogIGZvcmVjYXN0KGg9MzApDQpmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQpKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBWZW50YSBkZSBjb21pZGEgbWlub3Jpc3RhIikgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiICgiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpgYGANCg0KU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVsIG1ldG9kbyBTYXJpbWEgc2UgYWp1c3RhIG1lam9yIGEgYW1iYXMgc2VyaWVzIGRlIHRpZW1wby4NCg0KDQojIENvbmNsdXNpw7NuDQoNCiogRGUgZW50cmUgdG9kb3MgbG9zIG1vZGVsb3MgYmFzaWNvcyBkZSBwcm9uw7NzdGljb3MgcGFyYSBsYSBzZXJpZSBkZSB0aWVtcG8gZGUgbGFzIFZlbnRhcyBtaW5vcmlzdGFzIGRlIHRpZW5kYXMgZGUgcm9wYSB5IGFjY2Vzb3Jpb3MgZGUgcm9wYSwgZWwgcXVlIG1lam9yIHNlIGFqdXN0YWJhIGluaWNpYWxtZW50ZSBlcyBlbCBTZWFzb25hbCBOYcOvdmUuDQoNCiogUGFyYSBlbCBtZXRvZG8gZGUgc3Vhdml6YWNpb24gZXhwcG9uZW5jaWFsLCBlbCBxdWUgbWVqb3Igc2UgYWp1c3RhYmEgYSBsb3MgZGF0b3MgZnVlIGVsIG1vZGVsbyBkZSBIb2x0LVdpbnRlcnMgQW1vcnRpZ3VhZG8uDQoNCiogRWwgbcOpdG9kbyBEYW1wZWQgeSBBcmltYSB0ZXJtaW5hbiBwb3Igc2VyIG1lam9yZXMgbW9kZWxvcyBwYXJhIHByb25vc3RpY2FyLCBwZXJvIGVsIG3DqXRvZG8gQXJpbWEgRXN0YWNpb25hbCBlcyBlbCBtZWpvcjsgdGllbmUgdW4gbWVqb3IgYWp1c3RlIHNlZ8O6biBsb3MgZGF0b3MgZW4gYW1iYXMgc2VyaWVzIGRlIHRpZW1wby4NCg0KKiBQb3Igw7psdGltbyBwb2RlbW9zIGNvbmNsdWlyIHF1ZTogDQogIC0gUGFyYSBsYSBzZXJpZSBkZSB0aWVtcG8gZGUgbGEgY29taWRhIG1pbm9yaXN0YToNCiAgTW9kZWxvIEhvbHQgV2ludGVycyBBbW9ydGlndWFkbyBlcyBtdXkgYnVlbm8sIHBlcm8gZWwgbWVqb3IgZXMgQXJpbWEgZXN0YWNpb25hbCAoU2FyaW1hKSBwb3JxdWUgdGllbmUgICAgIGxvcyBlcnJvcmVzIGRlIHByb27Ds3N0aWNvIG3DoXMgcGVxdWXDsW9zLg0KDQogIC0gUGFyYSBsYSBzZXJpZSBkZSB0aWVtcG8gZGUgbGEgdmVudGEgZGUgcm9wYToNCiAgRGFtcGVkIGZ1ZSBtdXkgYnVlbmEsIHBlcm8gbGEgbWVqb3IgZXMgQXJpbWEgZXN0YWNpb25hbCAoU2FyaW1hKSBwb3JxdWUgdGllbmUgbG9zIGVycm9yZXMgZGUgcHJvbsOzc3RpY28gbcOhcyBwZXF1ZcOxb3MuDQogIA==